﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ESpace;
using System.IO;
using System.Net;
using Ord;

namespace Ordonnance
{
    public partial class FormConn : Form
    {
        FormMain frm;
        public String ClientName;
        public String ClientPass;

        String fPathLog;

        #region *** настройки *****************************************
        public ESettings sett;
        public bool bSound
        {
            get { return sett.GetSettings("Звук", "1") == "1"; }
            set
            {
                if (value) sett.SetSettings("Звук", "1");
                else sett.SetSettings("Звук", "0");
            }
        }
        public bool bChatHist
        {
            get { return sett.GetSettings("Вести историю чата", "1") == "1"; }
            set
            {
                if (value) sett.SetSettings("Вести историю чата", "1");
                else sett.SetSettings("Вести историю чата", "0");
            }
        }
        #endregion *** настройки *****************************************

        public FormConn()
        {
            InitializeComponent();

            // логи
            if (!Directory.Exists(Application.StartupPath + "\\Logs")) Directory.CreateDirectory(Application.StartupPath + "\\Logs");

            fPathLog = Application.StartupPath + "\\Logs\\Log_" + DateTime.Now.ToString("yyyy.MM.dd_hh.mm.ss") + ".log";

            frm = new FormMain(this);

            //LoadConnectSettings(clnt);

            // загрузка настроек
            sett = new ESettings("Settings.txt");
            String strIp = sett.GetSettings("IP Сервера", "127.0.0.1");
            IPAddress ip = ETcp.GetAddr(strIp);
            if (ip == null) clnt.ServerIP = "127.0.0.1";
            else clnt.ServerIP = strIp;
            int n = 0;
            if (!int.TryParse(sett.GetSettings("Порт Сервера", "654"), out n)) clnt.ServerPort = 654;
            else clnt.ServerPort = n;
        }

        void LoadConnectSettings(ETcpClient client)
        {
            String fName = "ConnectSettings.txt";
            if (!File.Exists(fName))
            {
                String[] strl = new String[2];
                strl[0] = "127.0.0.1 //IP-адрес сервера";
                strl[1] = "654 //порт сервера";
                File.WriteAllLines(fName, strl, Encoding.Default);
            }

            // загружаем настройки
            String[] sl = File.ReadAllLines(fName, Encoding.Default);
            if (sl.Length != 2) return;
            String[] sl1 = sl[0].Split(' ');
            if (sl1.Length < 1) return;
            String strIp = sl1[0];
            IPAddress ip = ETcp.GetAddr(strIp);
            if (ip == null) return;
            int p = 0;
            sl1 = sl[1].Split(' ');
            if (sl1.Length < 1) return;
            if (!int.TryParse(sl1[0], out p)) return;

            client.ServerIP = strIp;
            client.ServerPort = p;
        }

        // логин
        private void button1_Click(object sender, EventArgs e)
        {
            /*Hide();
            frm.Show();*/
            // пробуем логиниться
            Login(false);
        }

        // регистрация
        private void button2_Click(object sender, EventArgs e)
        {
            if (txbName.Text.Trim() == "")
            {
                MessageBox.Show("Введите имя для регистрации");
                return;
            }
            if (txbPass.Text.Trim() == "")
            {
                MessageBox.Show("Введите пароль для регистрации");
                return;
            }
            if (MessageBox.Show("Вы уверены, что у вас нет уже юзера и вам действительно нужно региться?", "Вопрос", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == System.Windows.Forms.DialogResult.Yes)
            {
                Login(true);
            }
        }

        // логин и релогин с формы логина
        public void Login(bool bAdd)
        {
            ClientName = txbName.Text.Trim();
            ClientPass = txbPass.Text.Trim();
            if (ClientName == "" || ClientPass == "")
            {
                MessageBox.Show("Имя или пароль пусты");
                return;
            }

            if (!clnt.IsConnect)
            {
                if (!clnt.Connect())
                {
                    MessageBox.Show("Невозможно подключиться к серверу");
                    return;
                }
            }

            // отправляем данные
            BufWriter bw = new BufWriter();
            if (bAdd) bw.WriteByte(2);
            else bw.WriteByte(1);
            bw.WriteString(ClientName);
            bw.WriteString(ClientPass);

            if (!clnt.Send(bw.buf))
            {
                MessageBox.Show("Сервер недоступен. Попробуйте еще раз через несколько минут.");
            }
            if (bAdd) AddLog("Отправлены данные регистрации: " + ClientName + ", " + ClientPass);
            else AddLog("Отправлены данные для логина: " + ClientName + ", " + ClientPass);
        }

        // повторный логин при рассоединении. Вызывается автоматически при дисконнектах после загрузки главной формы
        void ReLogin()
        {
            isLogin = false; // сразу говорим, что логина нет
            // если есть коннект, делаем дисконнект
            if (clnt.IsConnect) clnt.Disconnect();
            // пробуем коннектиться
            AddLog("Релогин: пробуем подконнектиться к серверу");
            // если не получилось, запускаем таймер реконекта и выходим
            if (!clnt.Connect())
            {
                tmrReconnect.Start();
                AddLog("Релогин: не удалось подконнектиться к серверу. Запускаем таймер релогина");
                return;
            }
            else AddLog("Релогин: подключение к серверу удалось. Отправляем данные логина");
            // отправляем данные в случае успешного коннекта к серверу
            BufWriter bw = new BufWriter();
            bw.WriteByte(1);
            bw.WriteString(ClientName);
            bw.WriteString(ClientPass);
            clnt.Send(bw.buf);
        }

        #region *** принятие сообщений **********************************************

        private void cl_OnRecive(object Sender, ESpace.ETcpClient.RecvEventArgs e)
        {
            byte[] buf = e.buf;
            try
            {
                // проверочный пакет
                bSendCheckPack = true;

                bool bUseData = true;
                switch (buf[0])
                {
                    case 1: { LogIn(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - Логин. buf[1] = {1}", buf[0], buf[1])); break; }
                    // регистрация
                    case 2: { Registration(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - Регистрация. buf[1] = {1}", buf[0], buf[1])); break; }
                    // обновить список подключенных юзеров
                    case 3: { UpdateListUsers(buf); AddLog(string.Format("Получен пакет c id {0}. Обновить список подключенных юзеров", buf[0])); break; }
                    // сообщение от сервера
                    case 4: { ServerMassage(buf); AddLog(string.Format("Получен пакет c id {0}. Сообщение в чат от сервера", buf[0])); break; }
                    // сообщение от юзера в чат
                    case 5: { ClientMassage(buf); AddLog(string.Format("Получен пакет c id {0}. Сообщение в чат от клиента", buf[0])); break; }
                    // смена пароля
                    case 6: { AnsChangePass(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - Смена пароля. buf[1] = {1}", buf[0], buf[1])); break; }
                    // ответ - создание новой игры (20)
                    case 20: { AnsCreateGame(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - Создание игры. buf[1] = {1}", buf[0], buf[1])); break; }
                    // ответ - удаление игры (21)
                    case 21: { AnsDeleteGame(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - Удаление игры. buf[1] = {1}", buf[0], buf[1])); break; }
                    // ответ - удаление пользователя из игры (22)
                    case 22: { AnsDeleteUserInGame(buf); AddLog(string.Format("Получен пакет c id {0}. Удаление пользователя из игры", buf[0])); break; }
                    // ответ - заявка на участие в игре (23)
                    case 23: { AnsAddIntoGame(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - Заявка на участие в игре", buf[0])); break; }
                    // ответ - отмена заявки на участие в игре (24)
                    case 24: { AnsCancelIntoGame(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - отмена заявки на участие в игре", buf[0])); break; }
                    // ответ - начало игры (25)
                    case 25: { AnsBeginGame(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - начало игры", buf[0])); break; }
                    // обновление списка игр
                    case 26: { UpdateListGames(buf); AddLog(string.Format("Получен пакет c id {0}. Обновление списка игр", buf[0])); break; }
                    // ответ - добавление бота
                    case 27: { AnsAddBot(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - добавление бота", buf[0])); break; }
                    // обновление списка доступных на сервере ботов
                    case 28: { UpdateBotList(buf); AddLog(string.Format("Получен пакет c id {0}. обновление списка доступных на сервере ботов", buf[0])); break; }
                    // ответ - выложить карту на стол
                    case 50: { AnsPlayCard(buf); AddLog(string.Format("Получен пакет c id {0}. ответ - выложить карту на стол. buf[1] = {1}", buf[0], buf[1])); break; }
                    // обновление стола
                    case 51: { UpdateTable(buf); AddLog(string.Format("Получен пакет c id {0}. обновление стола", buf[0])); break; }

                    default: { bUseData = false; break; }
                }

                // сброс актуального IDActQuery
                if (buf[0] == IDActQuery)
                {
                    IDActQuery = -1;
                    bUseData = true;
                    sendL.RemoveAt(0);
                    AddLog(string.Format("Получен пакет c id {0}. сброс актуального IDActQuery", buf[0]));
                }

                if (!bUseData)
                    AddLog("ПОЛУЧЕН НЕИЗВЕСТНЫЙ ПАКЕТ С ID " + buf[0].ToString());
            }
            catch (Exception ex)
            {
                MessageBox.Show("Ошибка приема данных: " + ex.Message);

                // если ошибка, убираем вместе с запросом
                if (buf[0] == IDActQuery)
                {
                    sendL.RemoveAt(0);
                    IDActQuery = -1;
                }
            }

        }

        #region *** обновления ************************************************

        void UpdateListUsers(byte[] buf)
        {
            bool bErr = false;
            List<String> sl = new List<String>();
            BufReader br = new BufReader(buf);
            try
            {
                br.ReadByte();
                int kolvo = br.ReadInt();
                for (int i = 0; i < kolvo; i++)
                {
                    String s = br.ReadString();
                    if (s == "")
                    {
                        bErr = true;
                    }
                    sl.Add(s);

                }
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета Обновление листа юзеров >> " + ex.Message);
                bErr = true;
            }
            sl.Sort();
            frm.UpdateChatList(sl, bErr);

        }

        void UpdateListGames(byte[] buf)
        {
            BufReader br = new BufReader(buf);
            br.ReadByte();
            UpdListGames(br);
        }

        bool UpdListGames(BufReader br)
        {
            bool bErr = false;
            List<Game> gmL = new List<Game>();
            try
            {
                int kolvoGames = br.ReadInt();
                for (int i = 0; i < kolvoGames; i++)
                {
                    String nm = br.ReadString(); // имя игры
                    int kolvoUsers = br.ReadInt(); // количество юзверей
                    bool bPlay = br.ReadByte() == 1; // игра запущена
                    String Moder = br.ReadString(); // модератор игры
                    gmL.Add(new Game(nm, Moder));
                    int ind = gmL.Count - 1;
                    gmL[ind].GameIsPlay = bPlay;
                    // список игроков
                    for (int j = 0; j < kolvoUsers; j++)
                    {
                        String nmPl = br.ReadString();
                        int botInd = br.ReadByte();
                        gmL[ind].pl.Add(new Player(nmPl, botInd));
                    }
                }
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета Обновление листа игр >> " + ex.Message);
                bErr = true;
            }
            frm.UpdateGameList(gmL, bErr);
            return !bErr;
        }

        void UpdateBotList(byte[] buf)
        {
            BufReader br = new BufReader(buf);
            br.ReadByte();
            Gl.BotList.Clear(); ;
            int kolvo = br.ReadInt();
            for (int i = 0; i < kolvo; i++)
                Gl.BotList.Add(br.ReadByte(), br.ReadString());
            frm.UpdateBotList();
        }

        void UpdateTable(byte[] buf)
        {
            try
            {
                BufReader br = new BufReader(buf);
                br.ReadByte();
                br.ReadByte();
                UpdTbl(br);
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета Обновление стола. (в пакете меньше 2-х байт) >> " + ex.Message);
            }
        }

        bool UpdTbl(BufReader br)
        {
            bool bErr = false;
            // игра
            String GameName = "";
            // карты в руке
            List<Card> myCrd = new List<Card>();
            // стол
            List<Player> pl = new List<Player>();
            // текущий игрок
            byte PlayPl = 0;

            try
            {
                // игра
                GameName = br.ReadString();
                // количество карт в руке
                int kolvoCrd = br.ReadByte();
                // читаем непрерывный массив данных по картам
                bool[] cMas = br.ReadBitMas();
                int i = 0;
                // заполняем карты в руке
                for (int c = 0; c < kolvoCrd; c++)
                {
                    Card crd = new Card();
                    for (int tr = 0; tr < 5; tr++)
                    {
                        crd.troop[tr] = cMas[i];
                        i++;
                    }
                    myCrd.Add(crd);
                }

                // количество игроков
                int kolvoPl = br.ReadByte();

                // смотрим количество карт в каждом городе каждого игрока
                int[,] kolvoC = new int[kolvoPl, 5];
                for (int p = 0; p < kolvoPl; p++)
                {
                    pl.Add(new Player(p.ToString())); // имена потом заменятся
                    for (int t = 0; t < 5; t++)
                    {
                        kolvoC[p, t] = br.ReadByte();
                    }
                }

                // читаем непрерывный массив данных по картам
                cMas = br.ReadBitMas();
                // формируем карты каждого игрока
                i = 0;
                for (int p = 0; p < kolvoPl; p++)
                {
                    for (int t = 0; t < 5; t++)
                    {
                        for (int c = 0; c < kolvoC[p, t]; c++)
                        {
                            Card crd = new Card();
                            for (int tr = 0; tr < 5; tr++)
                            {
                                crd.troop[tr] = cMas[i];
                                i++;
                            }
                            pl[p].town[t].card.Add(crd);
                        }
                    }
                }

                // формируем последние карты каждого игрока
                for (int p = 0; p < kolvoPl; p++)
                {
                    pl[p].LastCard.X = br.ReadInt();
                    pl[p].LastCard.Y = br.ReadInt();
                }

                // текущий игрок
                PlayPl = br.ReadByte();
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета Обновление стола >> " + ex.Message);
                bErr = true;
            }
            // вызываем фию основной формы
            frm.UpdateTable(GameName, pl, PlayPl, myCrd, bErr);
            return !bErr;
        }

        void ServerMassage(byte[] buf)
        {
            BufReader br = new BufReader(buf);
            String mess = "";
            bool bErr = false;
            try
            {
                br.ReadByte();
                mess = br.ReadString();
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета Сообщение от сервера >> " + ex.Message);
                bErr = true;
            }
            frm.RecvNewUserMessage("Сервер", mess, bErr);
        }

        void ClientMassage(byte[] buf)
        {
            BufReader br = new BufReader(buf);
            String uName = "";
            String mess = "";
            bool bErr = false;
            try
            {
                br.ReadByte();
                uName = br.ReadString();
                mess = br.ReadString();
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета Сообщение от клиента >> " + ex.Message);
                bErr = true;
            }
            frm.RecvNewUserMessage(uName, mess, bErr);
        }

        #endregion *** обновления ************************************************

        #region *** ответы *********************

        void LogIn(byte[] buf)
        {
            if (buf[1] != 0)
            {
                if (buf[1] == 3)
                {
                    MessageBox.Show("Ошибка логина: Такой пользователь уже залогинен на этом сервере");

                }
                else
                {
                    MessageBox.Show("Ошибка логина: Пользователь с таким логином и паролем не зарегистрирован.");
                }
            }
            // удачный логин
            if (buf[1] == 0)
            {
                isLogin = true;
                if (tmrReconnect.Enabled)
                {
                    tmrReconnect.Stop(); // если произошел логин, останавливаем цикл релогина
                    AddLog("Останавливаем цикл релогина");
                }
                // если основная форма еще не открыта
                if (Visible)
                {
                    frm.Text = ClientName;
                    Hide();
                    frm.Show();
                    tmrSend.Start(); // запускаем постоянную проверку коннекта
                    AddLog("Открываем основную форму и запускаем постоянную проверку коннекта");
                }
            }
        }

        void Registration(byte[] buf)
        {
            if (buf[1] == 0)
            {
                MessageBox.Show("Регистрация прошла успешно. Для входа в чат к серверу нажмите 'Войти'");
            }
            if (buf[1] == 1)
            {
                MessageBox.Show("Регистрация не удалась, поскольку логин или пароль пустые. Проверьте данные и попробуйте еще.");
            }
            if (buf[1] == 2)
            {
                MessageBox.Show("Пользователь с таким именем уже существует'");
            }
        }

        byte ParseAnswerUpdListGames(String packName, byte[] buf)
        {
            byte err = 0;
            try
            {
                err = buf[1];
                if (err == 0)
                {
                    BufReader br = new BufReader(buf);
                    br.ReadByte();
                    err = br.ReadByte();
                    if (err == 0)
                    {
                        if (!UpdListGames(br))
                        {
                            err = 255;
                            AddLog("Ошибка разбора пакета " + packName + " >> ошибка обновления игр");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета " + packName + " >> " + ex.Message);
                err = 255;
            }
            return err;
        }

        // ответ - создание новой игры (20)
        void AnsCreateGame(byte[] buf)
        {
            byte err = ParseAnswerUpdListGames("Создание новой игры", buf);
            frm.CreateGame(err);
        }

        // ответ - удаление игры (21)
        void AnsDeleteGame(byte[] buf)
        {
            byte err = ParseAnswerUpdListGames("Удаление игры", buf);
            frm.DeleteGame(err);
        }

        // ответ - удаление пользователя из игры (22)
        void AnsDeleteUserInGame(byte[] buf)
        {
            byte err = ParseAnswerUpdListGames("Удаление пользователя из игры", buf);
            frm.DeleteUserInGame(err);
        }

        // ответ - заявка на участие в игре (23)
        void AnsAddIntoGame(byte[] buf)
        {
            byte err = ParseAnswerUpdListGames("Заявка на участие в игре", buf);
            frm.EnterIntoGame(err);
        }

        // ответ - отмена заявки на участие в игре (24)
        void AnsCancelIntoGame(byte[] buf)
        {
            byte err = ParseAnswerUpdListGames("Отмена заявки на участие в игре", buf);
            frm.CanselIntoGame(err);
        }

        // ответ - начало игры (25)
        void AnsBeginGame(byte[] buf)
        {
            byte err = ParseAnswerUpdListGames("Запуск игры", buf);
            frm.BeginGame(err);
        }

        // ответ - добавление бота(27)
        void AnsAddBot(byte[] buf)
        {
            byte err = ParseAnswerUpdListGames("Добавление бота", buf);
            frm.AddBot(err);
        }

        // ответ - положить карту на стол (50)
        void AnsPlayCard(byte[] buf)
        {
            byte err = 0;
            try
            {
                if (buf[1] == 0)
                {
                    BufReader br = new BufReader(buf);
                    br.ReadByte();
                    err = br.ReadByte();
                    if (err == 0)
                    {
                        if (!UpdTbl(br))
                        {
                            err = 255;
                            AddLog("Ошибка разбора пакета создание новой игры >> ошибка обновления игр");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                AddLog("Ошибка разбора пакета создание новой игры >> " + ex.Message);
                err = 255;
            }
            frm.PlayCard(err);
        }

        void AnsChangePass(byte[] buf)
        {
            byte err = 0;
            if (buf.Length != 2)
            {
                err = 255;
                AddLog("Ошибка разбора пакета смена пароля >> Массив имеет неверную длину");
            }
            else err = buf[1];
            frm.ChangePass(err);
        }

        #endregion *** ответы *********************

        #endregion *** принятие сообщений **********************************************

        // релогин каждые 3 секунды (если сервер недоступен)
        private void tmrReconnect_Tick(object sender, EventArgs e)
        {
            if (!serverIsAvailable)
            {
                AddLog("Очередной релогин по таймеру");
                ReLogin();
            }
            if (serverIsAvailable)
            {
                tmrReconnect.Stop();
                AddLog("Сервер вновь доступен. Таймер релогина остановлен");
            }
        }

        List<byte[]> sendL = new List<byte[]>();
        public bool IsWaitAnswerFromServer { get { return IDActQuery != -1; } }
        int IDActQuery = -1;
        private void tmrSend_Tick(object sender, EventArgs e)
        {
            if (!serverIsAvailable || IsWaitAnswerFromServer)
            {
                return;
            }

            // если есть, что отправлять, отправляем и сбрасываем счетчик ожидания
            if (sendL.Count > 0)
            {
                IDActQuery = sendL[0][0];
                // пробуем отправить. В случае неудачи запусткаем релогин
                AddLog("Отправляем очередное сообщение на сервер. Id=" + IDActQuery);
                if (!clnt.Send(sendL[0]))
                {
                    AddLog("Не удалось отправить очередное сообщение на сервер. Запускаем релогин");
                    IDActQuery = -1;
                    ReLogin();
                }
            }
        }

        #region *** Команды. (функции, вызывающиеся формой Main) *********************

        bool isLogin = false;

        public bool serverIsAvailable
        {
            get
            {
                return clnt.IsConnect & isLogin;
            }
        }

        public enum Ansv { Ok, ServerIsNotAvailable }

        // отправка сообщения в чат (5)
        public void SendUserMessage(String Message)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(5);
            bw.WriteString(ClientName);
            bw.WriteString(Message);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Сообщение в чат (5)");
        }

        // команда смены пароля (6)
        public void SendChangePass(String oldPass, String newPass)
        {
            BufWriter bw = new BufWriter();
            bw.WriteByte(6);
            bw.WriteString(oldPass);
            bw.WriteString(newPass);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Команда смены пароля (6)");
        }

        // команда добавления игры (20)
        public void SendAddGame(String GameName)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(20);
            bw.WriteString(GameName);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Добавление игры (20)");
        }

        // удалить игру (21)
        public void SendDeleteGame(String GameName)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(21);
            bw.WriteString(GameName);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Удаление игры (21)");
        }

        // команда удалить юзера из игры (22)
        public void SendDelUserInGame(String GameName, String UserName)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(22);
            bw.WriteString(GameName);
            bw.WriteString(UserName);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Удалить юзера из игры (22)");
        }

        // команда подать заявку на участие (23)
        public void SendAddIntoGame(String GameName)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(23);
            bw.WriteString(GameName);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Подать заявку на участие (23)");
        }

        // отменить заявку на участие (24)
        public void SendCancelIntoGame(String GameName)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(24);
            bw.WriteString(GameName);
            bw.WriteString(ClientName);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Отменить заявку на участие (24)");
        }

        // начать игру (25)
        public void SendBeginGame(String GameName)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(25);
            bw.WriteString(GameName);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Запустить игру (25)");
        }

        // команда обновить список доступных игр (26)
        public void SendUpdateGameList()
        {
            // подготавливаем пакет
            byte[] buf = new byte[1] { 26 };
            SendOnServer(buf);
            AddLog("Добавляем в очередь отправки: Обновить список игр (26)");
        }

        // команда добавить бота (27)
        public void SendAddBot(String GameName, byte botInd)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(27);
            bw.WriteString(GameName);
            bw.WriteByte(botInd); // id типа бота
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Добавить бота (27)");
        }

        // команда добавления карты (50)
        public void SendCardInTable(String gmName, int NumCardInHand, int numTown)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(50);
            bw.WriteString(gmName);
            bw.WriteByte((byte)NumCardInHand);
            bw.WriteByte((byte)numTown);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Выложить карту в город (50)");
        }

        // команда обновления стола (51)
        public void SendUpdateTable(String gmName)
        {
            // подготавливаем пакет
            BufWriter bw = new BufWriter();
            bw.WriteByte(51);
            bw.WriteString(gmName);
            SendOnServer(bw.buf);
            AddLog("Добавляем в очередь отправки: Обновить стол (51)");
        }

        // отправка пакета на сервер
        void SendOnServer(byte[] buf)
        {
            sendL.Add(buf);
        }

        #endregion ***  Команды. (функции, вызывающиеся формой Main)  *********************

        bool bSendCheckPack = true;
        private void tmrCheckDisconnect_Tick(object sender, EventArgs e)
        {
            // если сервер доступен
            if (serverIsAvailable)
            {
                if (!bSendCheckPack)
                {
                    // если не пришел очередной проверочный пакет, пробуем отправить свой проверочный пакет
                    AddLog("Не пршел очередной проверочный пакет от сервера. Добавляем в список отправки проверочный пакет на сервер для проверки доступности");
                    byte[] buf = new byte[1] { 10 };
                    sendL.Add(buf);
                }
                // сбрасываем проверочный бит (он возведется при приеме следующего проверочного пакета от сервера)
                // проверочные пакеты от сервера должны идти каждые 10 секунд
                // таймер тикает каждые 20 секунд
                bSendCheckPack = false;
            }
            else bSendCheckPack = true;
        }

        private void FormConn_FormClosing(object sender, FormClosingEventArgs e)
        {
            clnt.Disconnect();
        }

        public void AddLog(String s)
        {
            File.AppendAllText(fPathLog, DateTime.Now.ToString() + " >> " + s + "\r\n", Encoding.Default);
        }

        FormEdit frmConnSett = new FormEdit();
        private void btnConnSett_Click(object sender, EventArgs e)
        {
            frmConnSett.el.Clear();
            frmConnSett.el.Add("IP Сервера", clnt.ServerIP);
            frmConnSett.el.Add("Порт Сервера", clnt.ServerPort.ToString());
            frmConnSett.NameOk = "OK";
            frmConnSett.Text = "Настройки подключения";
            frmConnSett.ShowDialog();
            if (frmConnSett.bOk)
            {
                String IP = frmConnSett.txb[0].Text.Trim();
                String Port = frmConnSett.txb[1].Text.Trim();

                if (ETcp.GetAddr(IP) == null)
                {
                    MessageBox.Show("Некоректный IP-адрес");
                    return;
                }
                int n = 0;
                int.TryParse(Port, out n);
                if (n < 1 || n > 65535)
                {
                    MessageBox.Show("Некоректный порт");
                    return;
                }

                bool bConn = false;
                if (clnt.IsConnect)
                {
                    clnt.Disconnect();
                    bConn = true;
                }
                clnt.ServerIP = IP;
                clnt.ServerPort = n;
                sett.SetSettings("IP Сервера", IP);
                sett.SetSettings("Порт Сервера", Port);
                if (bConn) clnt.Connect();
            }
        }

    }

}
